#include "BtreePlusNodeView.h"
#include "Page.h"

IBtreePlusNodeView::IBtreePlusNodeView(Page * page, uint32_t degree)
:page(page)
, degree(degree)
{
    assert(page != nullptr);
    assert(getNode() != nullptr);

    full = degree * 2;
}
IBtreePlusNodeView::~IBtreePlusNodeView(){
    page = nullptr;
}

BtreePlusNode * IBtreePlusNodeView::getNode() const { 
    return (BtreePlusNode*)(page->getAddr());
}
bool IBtreePlusNodeView::isLeaf() const{
    return getNode()->pageType == Leaf;
}
bool IBtreePlusNodeView::isInterior() const{
    return getNode()->pageType == Interior;
}

PageNumberType IBtreePlusNodeView::getPageNo() const {
    return page->getNo();
}
IBtreePlusNodeView::Type IBtreePlusNodeView::getType() const{
    return (Type)(getNode()-> pageType);
}
void IBtreePlusNodeView::setType(const IBtreePlusNodeView::Type type){
    if(type != getNode()->pageType){
        getNode()->pageType = type;
    }
}

PageNumberType IBtreePlusNodeView::getParent() const{
    return getNode()->parent;
}
void IBtreePlusNodeView::setParent(const PageNumberType parent){
    if(parent != getNode()->parent){
        getNode()->parent = parent;
    }
}
int IBtreePlusNodeView::getUsed() const{
    return getNode()->used;
}
uint16_t & IBtreePlusNodeView::usedRef(){
    return getNode()->used;
}
void IBtreePlusNodeView::reset(IBtreePlusNodeView::Type type){
    getNode()->pageType = type;
    getNode()->used = 0;
    getNode()->parent = 0;
}
BtreePlusLeafNodeView * IBtreePlusNodeView::CreateLeaf(Page * page){
    BtreePlusLeafNodeView * nv = new BtreePlusLeafNodeView(page);
    nv->reset(IBtreePlusNodeView::Leaf);
    nv->setNext(0);
    return nv;
}
BtreePlusInteriorNodeView * IBtreePlusNodeView::CreateInterior(Page * page){
    IBtreePlusNodeView * nv = new BtreePlusInteriorNodeView(page);
    nv->reset(IBtreePlusNodeView::Interior);
    return (BtreePlusInteriorNodeView*)nv;
}

IBtreePlusNodeView * IBtreePlusNodeView::Load(Page * page){
    assert(page != nullptr);
    BtreePlusNode * node = (BtreePlusNode*)(page->getAddr());
    if(node->pageType  == IBtreePlusNodeView::Leaf){
        return new BtreePlusLeafNodeView(page);
    }
    else{
        return new BtreePlusInteriorNodeView(page);
    }

}
BtreePlusLeafNodeView * IBtreePlusNodeView::LoadLeaf(Page * page){
    assert(page != nullptr);
    BtreePlusNode * node = (BtreePlusNode*)(page->getAddr());
    assert(node->pageType  == IBtreePlusNodeView::Leaf);
    return new BtreePlusLeafNodeView(page);
}
BtreePlusInteriorNodeView * IBtreePlusNodeView::LoadInterior(Page * page){
    assert(page != nullptr);
    BtreePlusNode * node = (BtreePlusNode*)(page->getAddr());
    assert(node->pageType  == IBtreePlusNodeView::Interior);
    return new BtreePlusInteriorNodeView(page);
}








BtreePlusLeafNodeView::BtreePlusLeafNodeView(Page * page)
:IBtreePlusNodeView(page, leafDegreeOfBtreePlus(page->getSize()))
{
}
BtreePlusLeafNodeView::~BtreePlusLeafNodeView(){
}

BtreePlusLeafNode * BtreePlusLeafNodeView::getLeafNode() const { 
    return (BtreePlusLeafNode*)(page->getAddr());
}
RowIDType BtreePlusLeafNodeView::getKey(const int index) const {
    if(index >= 0 && index < getUsed()){
        return getLeafNode()->cells[index].key;
    }
    return -1;
}
void BtreePlusLeafNodeView::_setKeyNoCheck(const int index, const RowIDType key) {
    getLeafNode()->cells[index].key = key;
}
int BtreePlusLeafNodeView::setKey(const int index, const RowIDType key) {
    if(index >= 0 && index < getUsed()){
        _setKeyNoCheck(index, key);
        return 0;
    }
    return -1;
}

PageNumberType BtreePlusLeafNodeView::getNext() const{
    return getLeafNode()->next;
}
int BtreePlusLeafNodeView::setNext(const PageNumberType next) {
    if(getLeafNode()->next != next){
        getLeafNode()->next = next;
        return 0;
    }
    return -1;
}
std::string BtreePlusLeafNodeView::getValue(const int index) const {
    if(index >= 0 && index < getUsed()){
        Data * pData = &(getLeafNode()->cells[index].data);
        return std::string(pData->content.raw, pData->dataLen);
    }
    return std::string();
}

void BtreePlusLeafNodeView::_setValueNoCheck(const int index, const std::string & data){
        assert(sizeof(::Data) >= data.size());
        Data * pData = &(getLeafNode()->cells[index].data);
        pData->dataLen = data.size();
        memcpy(pData->content.raw, data.c_str(), data.size());
}
int BtreePlusLeafNodeView::setValue(const int index, const std::string & data){
    if(index >= 0 && index < getUsed()){
        _setValueNoCheck(index, data);
        return 0;
    }
    return -1;
}

int BtreePlusLeafNodeView::append(const RowIDType key, const std::string & data){
    if( getUsed() < getTotal()){
        this->_setKeyNoCheck(getUsed(), key);
        this->_setValueNoCheck(getUsed(), data);
        ++usedRef();

        getPage()->setDirty();
        return 0;
    }
    return -1;//full
}

int BtreePlusLeafNodeView::insert(const RowIDType key, const std::string & data){
    if(getUsed() < getTotal()){
        int i = getUsed() - 1;
        for(; i >= 0; --i){
            const int64_t i_key = getKey(i);
            if(key > i_key){
                ++usedRef();
                _setKeyNoCheck(i+1, key);
                _setValueNoCheck(i+1, data);
                getPage()->setDirty();
                return 0;
            }
            else if(key < i_key) {
                getLeafNode()->cells[i+1] = getLeafNode()->cells[i];
            }
            else{ // key == i_key
                return -1;//Duplicated key
            }
        }
        if(i < 0){
            ++usedRef();
            _setKeyNoCheck(0, key);
            _setValueNoCheck(0, data);
            getPage()->setDirty();
            return 0;
        }
        assert(i < 0);
        return -1;
    }
    return -1;//full
}

int BtreePlusLeafNodeView::insert(const int index, const RowIDType key, const std::string & data){
    if(getUsed() < getTotal()){
        if(index >= 0 && index <= getUsed()){
            for(int i = getUsed(); i > index; --i){
                getLeafNode()->cells[i] = getLeafNode()->cells[i-1];
            }
            _setKeyNoCheck(index, key);
            _setValueNoCheck(index, data);

            ++usedRef();
            getPage()->setDirty();
            return 0;
        }
        return -1; //index out of range
    }
    return -1;//full
}
int BtreePlusLeafNodeView::del(const int index) {
    if(index >= 0 && index < getUsed()){
        for( int i = index; i < getUsed()-1; ++i){
            getLeafNode()->cells[i] = getLeafNode()->cells[i+1];
        }
        --usedRef();

        getPage()->setDirty();
        return 0;
    }
    return -1;
}
std::string BtreePlusLeafNodeView::toString() const {
    std::ostringstream oss;
    oss << std::dec 
        << "type:leaf\t"
        << " degree:"   << getDegree() << " full:" << getTotal()
        << " used:" << getUsed() << " parent:" << getParent()
        << " next:" << getNext()
        << std::endl
        ;
        for(int i = 0; i < getUsed(); ++i){
            oss << "\t" << i << " key:[" << getKey(i) << "] data:["  << getValue(i) << "]" << std::endl;
        }
    return oss.str();
}


void BtreePlusLeafNodeView::test(){
    std::cout << std::dec  << "BtreePlusLeafNodeView::testing..." << std::endl;
    auto page = Page::newPage(0x10*0x400);
    auto nv = IBtreePlusNodeView::CreateLeaf(page);
    std::cout << nv->toString() << std::endl;
    Page::freePage(page);
}



BtreePlusInteriorNodeView::BtreePlusInteriorNodeView(Page * page)
:IBtreePlusNodeView(page, interiorDegreeOfBtreePlus(page->getSize()))
{
}
BtreePlusInteriorNodeView::~BtreePlusInteriorNodeView(){
}

BtreePlusInteriorNode * BtreePlusInteriorNodeView::getInteriorNode() const { 
    return (BtreePlusInteriorNode*) (page->getAddr()); 
}
RowIDType BtreePlusInteriorNodeView::getKey(const int index) const {
    if(index >= 0 && index < getUsed()){
        return getInteriorNode()->cells[index].key;
    }
    return -1;
}
void BtreePlusInteriorNodeView::_setKeyNoCheck(const int index, const RowIDType key) {
    getInteriorNode()->cells[index].key = key;
}
int BtreePlusInteriorNodeView::setKey(const int index, const RowIDType key) {
    if(index >= 0 && index < getUsed()){
        _setKeyNoCheck(index, key);
        return 0;
    }
    return -1;
}
PageNumberType BtreePlusInteriorNodeView::getChild(const int index) const{
    if(index >= 0 && index < getUsed()){
        return getInteriorNode()->cells[index].child;
    }
    return -1;
}
void BtreePlusInteriorNodeView::_setChildNoCheck(const int index, const PageNumberType child){
    getInteriorNode()->cells[index].child = child;
}
int BtreePlusInteriorNodeView::setChild(const int index, const PageNumberType child){
    if(index >= 0 && index < getUsed()){
        _setChildNoCheck(index, child);
        return 0;
    }
    return -1;
}

int BtreePlusInteriorNodeView::append(const RowIDType key, const PageNumberType child){
    if( getUsed() < getTotal()){
        _setKeyNoCheck(getUsed(), key);
        _setChildNoCheck(getUsed(), child);
        ++usedRef();
        getPage()->setDirty();
        return 0;
    }
    return -1;//full
}
int BtreePlusInteriorNodeView::insert(const int index, const RowIDType key, const PageNumberType child){
    if(getUsed() < getTotal()){
        if(index >= 0 && index <= getUsed()){
            for(int i = getUsed(); i > index; --i){
                getInteriorNode()->cells[i] = getInteriorNode()->cells[i-1];
            }
            _setKeyNoCheck(index, key);
            _setChildNoCheck(index, child);

            ++usedRef();
            getPage()->setDirty();
            return 0;
        }
        return -1; //index out of range
    }
    return -1;//full
}
int BtreePlusInteriorNodeView::del(const int index){
    if(getUsed() > 0){
        if(index >= 0 && index < getUsed()){
            for( int i = index; i < getUsed()-1; ++i){
                getInteriorNode()->cells[i] = getInteriorNode()->cells[i+1];
            }
            --usedRef();
            getPage()->setDirty();
            return 0;
        }
        return -1; //index out of range
    }
    return -1; //empty
}
std::string BtreePlusInteriorNodeView::toString() const {
    std::ostringstream oss;
    oss << std::dec 
        << "type:interior\t"
        << " degree:"   << getDegree() << " full:" << getTotal()
        << " used:" << getUsed() << " parent:" << getParent()
        << std::endl
        ;
        for(int i = 0; i < getUsed(); ++i){
            oss << "\t" << i << " key:[" << getKey(i) << "] child:["  << getChild(i) << "]" << std::endl;
        }
    return oss.str();
}


static void testBase(){
    auto page = Page::newPage(0x10*0x400);

    auto nv = IBtreePlusNodeView::CreateInterior(page);
    assert( 0 == nv->append(1, 2));
    assert(nv->getUsed() == 1 && nv->getKey(0) == 1 && nv->getChild(0) == 2);

    assert(0 == nv->append(2, 3));
    assert(nv->getUsed() == 2 && nv->getKey(1) == 2 && nv->getChild(1) == 3);


    assert(0 == nv->insert(0, 0, 4));
    assert(nv->getUsed() == 3 && nv->getKey(0) == 0 && nv->getChild(0) == 4);
    assert(nv->getUsed() == 3 && nv->getKey(1) == 1 && nv->getChild(1) == 2);
    assert(nv->getUsed() == 3 && nv->getKey(2) == 2 && nv->getChild(2) == 3);
    assert(0 == nv->insert(nv->getUsed(), 4, 5));
    
    assert(0 == nv->del(nv->getUsed()-1));
    assert(nv->getUsed() == 3 && nv->getKey(0) == 0 && nv->getChild(0) == 4);
    assert(nv->getUsed() == 3 && nv->getKey(1) == 1 && nv->getChild(1) == 2);
    assert(nv->getUsed() == 3 && nv->getKey(2) == 2 && nv->getChild(2) == 3);

    assert(0 == nv->del(nv->getUsed()-1));
    assert(nv->getUsed() == 2 && nv->getKey(0) == 0 && nv->getChild(0) == 4);
    assert(nv->getUsed() == 2 && nv->getKey(1) == 1 && nv->getChild(1) == 2);

    assert(0 == nv->del(nv->getUsed()-1));
    assert(nv->getUsed() == 1 && nv->getKey(0) == 0 && nv->getChild(0) == 4);

    assert(0 == nv->del(nv->getUsed()-1));
    assert(nv->getUsed() == 0);

    assert(-1 == nv->del(nv->getUsed()-1));

    std::cout << nv->toString() << std::endl;

    Page::freePage(page);
}
static void testCreateTree(){
    std::cout << "BtreePlusInteriorNodeView::testCreateTree.." << std::endl;
    auto page = Page::newPage(0x10*0x400);

    {
        auto root = IBtreePlusNodeView::CreateLeaf(page);
        root->append(1, "hell0");
        root->append(2, "hell1");
        root->append(3, "hell2");

        std::cout << root->toString() << std::endl;
    }

    {
        auto root = IBtreePlusNodeView::CreateInterior(page);
        root->append(1, 2);
        root->append(2, 3);
        root->append(3, 4);
        std::cout << root->toString() << std::endl;
    }

    Page::freePage(page);
}

void BtreePlusInteriorNodeView::test(){
    std::cout << std::dec  << "BtreePlusInteriorNodeView::testing..." << std::endl;
    testBase();
    testCreateTree();
}
